package service

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"log"
	"strconv"
	"strings"
	"time"
	"web_api_gin/core"
	"web_api_gin/dao"
	"web_api_gin/dto"
	"web_api_gin/entity"
	"web_api_gin/util"
)

// SysUser 系统用户逻辑类
var SysUser = SysUserService{}

type SysUserService struct {
}

// 获取密码字符串
func (s *SysUserService) GetUserPassword(password string) (encrypt string, err error) {
	data := "gogf_web_admin:" + password

	return util.Md5Util.Encrypt([]byte(data))
}

// Login 系统用户登录服务类
func (s *SysUserService) Login(ctx *gin.Context, req dto.SysUsersApiSignReq) (*entity.SysUsers, error) {
	var email string
	if req.Email != "" {
		email = req.Email
	} else {
		email = req.UserName
	}
	sysUserData, err := dao.SysUser.FindByEmail(ctx, email)
	if err != nil {
		return nil, err
	}
	//判断用户信息是否为空
	if sysUserData == nil {
		return nil, errors.New("账号或密码错误")
	}
	//解密前端base64加密密码
	decodePassword, err := base64.StdEncoding.DecodeString(req.Password)
	if err != nil {
		log.Fatalln(err)
	}
	//加密用户数密码，用来匹配是否一致
	passwordInput, err := s.GetUserPassword(string(decodePassword))
	if err != nil {
		return nil, err
	}
	//密码错误
	if sysUserData.Password != passwordInput {
		return nil, errors.New("账号或密码错误")
	}
	return sysUserData, nil
}

// TokenCreate 创建用户token
func (s *SysUserService) TokenCreate(ctx *gin.Context, sysUserData *entity.SysUsers, tokenDefine interface{}) (interface{}, error) {
	if sysUserData == nil {
		return nil, errors.New("用户信息不存在")
	}
	var token interface{}
	if tokenDefine != nil {
		token = tokenDefine
	} else {
		tokenString := "sys_user:" + strconv.FormatUint(uint64(sysUserData.Id), 10)
		md5String, err := util.Md5Util.Encrypt([]byte(tokenString))
		if err != nil {
			return nil, err
		}
		token = md5String
	}
	//配置缓存
	cacheKey := "sys_user:" + token.(string)
	//缓存服务
	data, _ := json.Marshal(sysUserData)
	err := core.Redis.Set(ctx, cacheKey, data, 2*time.Hour).Err()
	if err != nil {
		return "", err
	}
	return token, nil
}

// AuthorizationGetUserData 通过分析请求中的head值获取当前请求用户数据
func (s *SysUserService) AuthorizationGetUserData(ctx *gin.Context) (*entity.SysUsers, string, error) {
	var authorizationArr []string
	//过滤分析用户token值
	authorization := ctx.Request.Header.Get("Authorization")
	if authorization != "" {
		authorizationArr = strings.Split(authorization, " ")
	}
	if len(authorizationArr) < 2 {
		return nil, "", errors.New("授权凭证不存在")
	}
	userToken := authorizationArr[1]
	userData, err := SysUser.TokenGetDataCache(ctx, userToken)
	if err != nil {
		return nil, userToken, err
	}
	if userData == nil {
		return nil, userToken, errors.New("用户不存在")
	}
	//设置用户信息
	ctx.Set("userData", userData)
	return userData, userToken, nil
}

// TokenGetDataCache 通过token获取用户数据
func (s *SysUserService) TokenGetDataCache(ctx *gin.Context, token string) (*entity.SysUsers, error) {
	//缓存服务
	cacheKey := "sys_user:" + string(token)
	userDataJsonStr, err := core.Redis.Get(ctx, cacheKey).Result()
	if err == redis.Nil {
		return nil, errors.New("用户登录已失效")
	} else if err != nil {
		return nil, err
	}
	if userDataJsonStr == "" {
		return nil, errors.New("用户登录信息已过期")
	}
	var cacheUserData *entity.SysUsers
	if err := json.Unmarshal([]byte(userDataJsonStr), &cacheUserData); err != nil {
		return nil, err
	}
	dbUserData, err := dao.SysUser.FindById(ctx, cacheUserData.Id)
	if err != nil {
		return nil, err
	}
	return dbUserData, nil
}

// 更新当前授权用户信息
func (s *SysUserService) AuthorizationUpUserData(ctx *gin.Context, data *entity.SysUsers) error {
	userData, _, err := s.AuthorizationGetUserData(ctx)
	if err != nil {
		return err
	}
	return dao.SysUser.UpdateUser(ctx, userData.Id, data)
}

// 重置用户密码
func (s *SysUserService) AuthorizationResetPwd(ctx *gin.Context, data *dto.SysUserResetPwdRequest) error {
	userData, _, err := s.AuthorizationGetUserData(ctx)
	if err != nil {
		return err
	}
	inputOldPwd, err := s.GetUserPassword(data.OldPassword)
	if err != nil {
		return err
	}
	if inputOldPwd != userData.Password {
		return errors.New("您输入的旧密码不正确")
	}
	if data.NewPassword != data.NewPassword {
		return errors.New("您输入的两次密码不一致，请确认后重试")
	}
	inputNewPwd, err := s.GetUserPassword(data.NewPassword)
	if err != nil {
		return err
	}
	if inputNewPwd == userData.Password {
		return errors.New("您输入的新密码与旧密码一致，未作改动")
	}
	return dao.SysUser.UpdateUser(ctx, userData.Id, &entity.SysUsers{Password: inputNewPwd, PasswordLevel: data.PasswordLevel})
}

// 判断当前用户是否有权限
func (s *SysUserService) AuthorizationHasPermission(ctx *gin.Context, permName string) bool {
	userData, _, err := s.AuthorizationGetUserData(ctx)
	if err != nil {
		return false
	}
	_, permList, err := s.GetAdminUserConf(ctx, userData, false)
	if err != nil {
		return false
	}
	permData, err := dao.SysPermission.GetPermByName(ctx, permName)
	if err != nil {
		return false
	}
	ctx.Set("permData", permData)
	if permData.PermId != 0 && permData.IsPublic == false {
		if util.ArrayUtil.IsContain(permList, permName) == true {
			return true
		} else {
			return false
		}
	}
	return true
}

// TokenDelete 删除清除token缓存
func (s *SysUserService) TokenDelete(ctx *gin.Context, token string) (interface{}, error) {
	//缓存服务
	rs, err := core.Redis.Del(ctx, token).Result()
	if err != nil {
		return nil, err
	}
	return rs, nil
}

// GetAdminUserConf 获取用户后台管理配置信息
func (s *SysUserService) GetAdminUserConf(ctx *gin.Context, userData *entity.SysUsers, isRefresh bool) ([]string, []string, error) {
	//角色名称缓存KEY
	cacheKey := "sys_user_perm_name_list:" + strconv.FormatUint(uint64(userData.Id), 10)
	userPermNameJsonStr, errCache := core.Redis.Get(ctx, cacheKey).Result()
	//用户权限名称列表
	var userPermNameList []string
	//获取用户角色名称列表和角色ID列表
	userRoleNameList, userRoleIdList, err := dao.SysRole.GetUserRoleListCache(ctx, userData, false)
	if err != nil {
		return nil, nil, err
	}
	if errCache == redis.Nil || isRefresh == true {
		userPermNameList, err := dao.SysPermission.GetUserPermission(ctx, userRoleNameList, userRoleIdList)
		if err != nil {
			return nil, nil, err
		}
		//缓存角色列表
		userPermNameJsonStr, _ := json.Marshal(userPermNameList)
		if err := core.Redis.Set(ctx, cacheKey, userPermNameJsonStr, 2*time.Hour).Err(); err != nil {
			return nil, nil, err
		}
		return userRoleNameList, userPermNameList, nil
	}
	if err := json.Unmarshal([]byte(userPermNameJsonStr), &userPermNameList); err != nil {
		return nil, nil, err
	}
	return userRoleNameList, userPermNameList, nil
}
